package cn.edu.xmu.orderpayment.payment.Service;

import cn.edu.xmu.oomall.core.util.ReturnNo;
import cn.edu.xmu.oomall.core.util.ReturnObject;
import cn.edu.xmu.orderpayment.payment.Dao.RefundDao;
import cn.edu.xmu.orderpayment.payment.constants.Constants;
import cn.edu.xmu.orderpayment.payment.microservice.wechatpayvo.WeChatPayRefundNotifyRetVo;
import cn.edu.xmu.orderpayment.payment.model.bo.Amount;
import cn.edu.xmu.orderpayment.payment.model.bo.Payment;
import cn.edu.xmu.orderpayment.payment.model.bo.PaymentPattern;
import cn.edu.xmu.orderpayment.payment.model.bo.Refund;
import cn.edu.xmu.orderpayment.payment.model.po.PaymentPo;
import cn.edu.xmu.orderpayment.payment.model.po.PaymentPoExample;
import cn.edu.xmu.orderpayment.payment.model.po.RefundPo;
import cn.edu.xmu.orderpayment.payment.model.po.RefundPoExample;
import cn.edu.xmu.orderpayment.payment.model.vo.ModificationBody;
import cn.edu.xmu.orderpayment.payment.model.vo.SimpleRefundRetVo;
import cn.edu.xmu.orderpayment.payment.mq.RocketMQService;
import cn.edu.xmu.orderpayment.payment.util.PayMentPatternLoader;
import cn.edu.xmu.orderpayment.payment.util.paypattern.BasePaymentPattern;
import cn.edu.xmu.privilegegateway.annotation.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

import static cn.edu.xmu.privilegegateway.annotation.util.Common.cloneVo;
import static cn.edu.xmu.privilegegateway.annotation.util.Common.setPoCreatedFields;

/**
 * @author  Gaunlin Wang 22920192204282
 * @date  2021-11-15
 */
@Service
public class RefundService {

    @Autowired(required = false)
    RefundDao refundDao;

    private static final Logger logger = LoggerFactory.getLogger(RefundDao.class);

    @Autowired
    @Lazy
    private PayMentPatternLoader payMentPatternLoader;

    @Autowired
    private RocketMQService rocketMQService;

    /**
     * 获取退款单的所有状态
     *
     * @return
     */
    @Transactional(readOnly = true, rollbackFor = Exception.class)
    public ReturnObject getRefundStates() {
        return refundDao.getRefundStates();
    }

    /**
     * 平台管理员修改退款信息
     * @param id
     * @param modificationBody
     * @param loginUserId
     * @param loginUserName
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject adminUpdateRefundInfo(Long id, ModificationBody modificationBody, Long loginUserId, String loginUserName){
        Refund bo = new Refund();
        //校验无误就为bo赋值
        if (modificationBody.getState() != null){
            if(modificationBody.getState().equals(Refund.State.RECONCILIATION_COMPLETED.getCode()) || modificationBody.getState().equals(Refund.State.LIQUIDATION_COMPLETED.getCode()))
                return new ReturnObject(ReturnNo.STATENOTALLOW, "当前状态禁止此操作");// 在业务逻辑层校验该退款单的状态
//            }// 这样做是否正确?
            bo.setState(modificationBody.getState());
        }
        if (modificationBody.getDescr() != null){
            bo.setDescr(modificationBody.getDescr());
        }
        bo.setId(id);
        return new ReturnObject(refundDao.updateRefund(bo, loginUserId, loginUserName));
    }

    /**
     * 通过DocumentId查询退款单
     * @param documentId
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject getRefundByDocumentId(String documentId) {
        ReturnObject<List<RefundPo>> refundReturnObject = refundDao.getRefundByDocumentId(documentId);
        if (refundReturnObject.getData() == null) return refundReturnObject;
        List<SimpleRefundRetVo> simpleRefundRetVoList = new ArrayList<>();
        for (RefundPo refundPo : refundReturnObject.getData()) {
            SimpleRefundRetVo simpleRefundRetVo = cloneVo(refundPo, SimpleRefundRetVo.class);
            simpleRefundRetVoList.add(simpleRefundRetVo);
        }
        return new ReturnObject<>(simpleRefundRetVoList);
    }

    /**
     * 平台管理员查询退款信息
     * 传入用户token,校验权限
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject adminGetRefundInfo(String documentId, Byte state, ZonedDateTime beginTime, ZonedDateTime endTime, Integer page, Integer pageSize) {
        RefundPoExample example = new RefundPoExample();
        RefundPoExample.Criteria criteria = example.createCriteria();
        if (documentId != null) {
            criteria.andDocumentIdEqualTo(documentId);
        }
        if (state != null) {
            criteria.andStateEqualTo(state);
        }
        if (beginTime != null) {
            criteria.andRefundTimeGreaterThanOrEqualTo(beginTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        if (endTime != null) {
            criteria.andRefundTimeLessThanOrEqualTo(endTime.withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime());
        }
        ReturnObject res = refundDao.getRefundInfo(example, page, pageSize);
        return res;
    }

    /**
     * 平台管理员查询退款详情
     * 根据id查退款单
     * 需验证该id对应的退款单是否存在
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class, readOnly = true)
    public ReturnObject adminGetCertainRefundInfo(Long id) {
        ReturnObject res = refundDao.getRefundDetailInfo(id);
        return res;
    }

    /**
     * 创建退款单
     * @param documentId
     * @param amount
     * @param documentType
     * @param descr
     * @return
     */
    public ReturnObject createRefund(String documentId, Long amount, Byte documentType, String descr) {
        PaymentPoExample paymentPoExample = new PaymentPoExample();
        PaymentPoExample.Criteria cr = paymentPoExample.createCriteria();
        cr.andDocumentIdEqualTo(documentId);
        cr.andStateNotEqualTo(Payment.State.PENDING_PAYMENT.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.CANCELED.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.PAYMENT_FAILED.getCode().byteValue());

        ReturnObject<List> returnObject = refundDao.getPaymentByExample(paymentPoExample);
        if (returnObject.getData() == null || returnObject.getData().size() != 1)
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, "没有支付单");
        PaymentPo paymentPo = (PaymentPo) returnObject.getData().get(0);

        Long realAmount = paymentPo.getAmount();

        Long hasRefund = 0L;
        ReturnObject<List<RefundPo>> refundReturnObject = refundDao.getRefundByDocumentId(documentId);
        if (!(refundReturnObject.getData() == null || refundReturnObject.getData().size() == 0)) {
            for (RefundPo refundPo : refundReturnObject.getData()) {
                hasRefund += refundPo.getAmount();
            }
        }

        if (amount + hasRefund > realAmount) return new ReturnObject(ReturnNo.REFUND_MORE);
//        BasePaymentPattern paymentPattern = getBasePayPatternById(paymentPo.getPatternId());

        Refund refund = new Refund();
        refund.setPatternId(paymentPo.getPatternId());
        refund.setAmount(amount);
        refund.setPaymentId(paymentPo.getId());
        refund.setDocumentId(documentId);
        refund.setDocumentType(Refund.DocumentType.getDocumentTypeByCode(Integer.valueOf(documentType)));
        refund.setState(Refund.State.PENDING_PAYMENT);
        refund.setDescr(descr);
        setPoCreatedFields(refund, paymentPo.getCreatorId(), paymentPo.getCreatorName());
///没有测到
        //ReturnObject<Refund> refundReturnObject1 = refundDao.createRefund(refund);
        //paymentPattern.refund(new Amount(amount, amount, "CNY"), paymentPo.getId().toString(), refundReturnObject1.getData().getId().toString(), descr);

        // 30s 延迟发消息检查第三方退款状态
        String json = JacksonUtil.toJson(refund);

        rocketMQService.sendRefundCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);

        return new ReturnObject<>();
    }

    /**
     * 建立全额退款
     * @param documentId
     * @param documentType
     * @param descr
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject createRefundAll(String documentId, Byte documentType, String descr) {
        PaymentPoExample paymentPoExample = new PaymentPoExample();
        PaymentPoExample.Criteria cr = paymentPoExample.createCriteria();
        cr.andDocumentIdEqualTo(documentId);
        cr.andStateNotEqualTo(Payment.State.PENDING_PAYMENT.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.CANCELED.getCode().byteValue());
        cr.andStateNotEqualTo(Payment.State.PAYMENT_FAILED.getCode().byteValue());

        ReturnObject<List> returnObject = refundDao.getPaymentByExample(paymentPoExample);
        if (returnObject.getData() == null || returnObject.getData().size() != 1)
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST, "没有支付单");
        PaymentPo paymentPo = (PaymentPo) returnObject.getData().get(0);


        Long hasRefund = 0L;
        ReturnObject<List<RefundPo>> refundReturnObject = refundDao.getRefundByDocumentId(documentId);
        if (!(refundReturnObject.getData() == null || refundReturnObject.getData().size() == 0)) {
            for (RefundPo refundPo : refundReturnObject.getData()) {
                hasRefund += refundPo.getAmount();
            }
        }

        if (hasRefund > 0) return new ReturnObject(ReturnNo.REFUND_MORE);
//        BasePaymentPattern paymentPattern = getBasePayPatternById(paymentPo.getPatternId());

        Refund refund = new Refund();
        refund.setPatternId(paymentPo.getPatternId());
        refund.setAmount(Long.valueOf(paymentPo.getActualAmount()));
        refund.setDocumentId(documentId);
        refund.setDocumentType(Refund.DocumentType.getDocumentTypeByCode(Integer.valueOf(documentType)));
        refund.setState(Refund.State.PENDING_PAYMENT);
        refund.setDescr(descr);
        setPoCreatedFields(refund, paymentPo.getCreatorId(), paymentPo.getCreatorName());
///没有测到
        //ReturnObject<Refund> refundReturnObject1 = refundDao.createRefund(refund);
        //paymentPattern.refund(new Amount(refund.getAmount(), refund.getAmount(), "CNY"), documentId, refundReturnObject1.getData().getId().toString(), descr);

        // 30s 延迟发消息检查第三方退款状态
        String json = JacksonUtil.toJson(refund);

        rocketMQService.sendRefundCheckMessage(json, Constants.FIVE_SECONDS_LEVEL);

        return new ReturnObject<>();

    }

    /**
     * 通过id获取支付渠道
     * @param id
     * @return
     */
//    private BasePaymentPattern getBasePayPatternById(Long id) {
//        try {
//            ReturnObject<PaymentPattern> ret = refundDao.getPaymentPatternById(id);
//            if (ret.getData() == null) return null;
//            BasePaymentPattern paymentPattern = payMentPatternLoader.getPaymentPatternByClassName(ret.getData().getClassName());
//            return paymentPattern;
//        } catch (Exception e) {
//            logger.error(e.getMessage());
//            return null;
//        }
//    }

    /**
     * 微信退款回调通知
     * 对收到的退款结果，修改相应退款单信息
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnObject wechatRefundNotify(WeChatPayRefundNotifyRetVo weChatPayRefundNotifyRetVo) {

        Refund refund = (Refund) refundDao.getRefundDetailInfo(Long.valueOf(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getOutRefundNo())).getData();
        if (refund == null) {
            return new ReturnObject(ReturnNo.RESOURCE_ID_NOTEXIST);
        }
        if (!refund.getState().equals(Refund.State.PENDING_PAYMENT)) {
            return new ReturnObject(ReturnNo.STATENOTALLOW);
        }

        if (weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundStatus().equals("SUCCESS")) {
            refund.setState(Refund.State.PAYMENT_COMPLETED.getCode());
            refund.setAmount(Long.valueOf(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getAmount().getRefund()));
//            // 退款成功通知订单
//            rocketMQService.sendSuccessOrderRefundMessage(refund.getDocumentId());
        }
        if (weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundStatus().equals("ABNORMAL")) {
            refund.setState(Refund.State.CANCELED.getCode());
        }
        refund.setTradeSn(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getRefundId());
        refund.setRefundTime(weChatPayRefundNotifyRetVo.getResource().getCiphertext().getSuccessTime());
        refund.setGmtModified(LocalDateTime.now());

        refundDao.updateRefund(refund, refund.getCreatorId(), refund.getCreatorName());
        return new ReturnObject();
    }

}
